package api

import (
	"os"
	"fmt"
	"math/big"
	"math/rand"
	"kumachan/standalone/rx"
	"kumachan/standalone/qt"
	"kumachan/standalone/ctn"
	"kumachan/standalone/util"
	"kumachan/interpreter/runtime/rt"
)


type CraftedReactive struct {
	In   rx.Sink
	Out  rx.Observable
}
func (c *CraftedReactive) Emit(obj rx.Object) rx.Observable {
	return c.In.Emit(obj)
}
func (c *CraftedReactive) Watch() rx.Observable {
	return c.Out
}
func (c *CraftedReactive) Read() rx.Observable {
	return c.Out.TakeOneAsSingleAssumeSync().Map(func(opt_ rx.Object) rx.Object {
		var opt = opt_.(rx.Optional)
		if opt.HasValue {
			return opt.Value
		} else {
			panic("something went wrong")
		}
	})
}
func (c *CraftedReactive) Update(f func(rx.Object)(rx.Object)) rx.Observable {
	return c.Read().SyncThen(func(old_state rx.Object) rx.Observable {
		return c.Emit(f(old_state))
	})
}

func Optional2Maybe(obj rx.Object) rt.Value {
	var opt = obj.(rx.Optional)
	if opt.HasValue {
		return rt.Some(opt.Value)
	} else {
		return rt.None()
	}
}

func CombineComputedAsTupleValues(items ([] rx.Observable)) rx.Observable {
	if len(items) <= 1 {
		panic("invalid argument")
	} else {
		return rx.CombineLatestWaitReady(items).Map(func(values_ rx.Object) rx.Object {
			var values = values_.([]rt.Value)
			return rt.TupleOf(values)
		})
	}
}

func CombineComputedAsSliceValues(items ([] rx.Observable)) rx.Observable {
	if len(items) == 0 {
		return rx.NewPersistent([]rt.Value{})
	} else {
		return rx.CombineLatestWaitReady(items)
	}
}

func recoverFromSyncCancellationPanic() {
	var err = recover()
	var _, is_sync_cancel = err.(rt.ExecutionCancelled)
	if err == nil || is_sync_cancel {
		// do nothing
	} else {
		panic(err)
	}
}


func __Sink(f rt.Value, h rt.InteropContext) rx.Sink {
	return rx.Callback(func(obj rx.Object) rx.Observable {
		return h.Call(f, obj).(rx.Observable)
	})
}
func __Blackhole() rx.Sink {
	return rx.BlackHole {}
}
func __SinkEmit(s rx.Sink, v rt.Value) rx.Observable {
	return s.Emit(v)
}
func __SinkAdapt(s rx.Sink, f rt.Value, h rt.InteropContext) rx.Sink {
	var adapter = func(obj rx.Object) rx.Object {
		return h.Call(f, obj)
	}
	return rx.SinkAdapt(s, adapter)
}
func __SinkDebounce(s rx.Sink, d *big.Int) rx.Observable {
	return rx.NewSync(func() (rx.Object, bool) {
		return rx.CreateBus(), true
	}).SyncThen(func(obj rx.Object) rx.Observable {
		var relay = obj.(rx.Bus)
		var d = util.GetUintNumber(d)
		return rx.NewSync(func() (rx.Object, bool) {
			return rx.Sink(relay), true
		}).With(rx.Connect(relay.Watch().DebounceTime(d), s))
	})
}

func __Bus(_ rt.Value) rx.Observable {
	return rx.NewSync(func() (rx.Object, bool) {
		return rx.CreateBus(), true
	})
}
func __BusIn(b rx.Bus) rx.Sink {
	return b
}
func __BusOut(b rx.Bus) rx.Observable {
	var r, is_reactive = b.(rx.Reactive)
	if is_reactive {
		return rx.DistinctWatch(r, rt.ValueEqual)
	} else {
		return b.Watch()
	}
}

func __Reactive(arg rt.TupleValue) rx.Observable {
	var initial = arg.Elements[0]
	return rx.NewSync(func() (rx.Object, bool) {
		return rx.CreateReactive(initial), true
	})
}
func __ReactiveFromSinkComputed(in rx.Sink, out rx.Observable) rx.Reactive {
	return &CraftedReactive {
		In:  in,
		Out: out,
	}
}
func __ReactiveUpdate(r rx.Reactive, f rt.Value, h rt.InteropContext) rx.Observable {
	return r.Update(func(old_state rx.Object) rx.Object {
		var new_state = h.Call(f, old_state)
		return new_state
	})
}
func __ReactiveMorph(r rx.Reactive, f rt.Value, g rt.Value, h rt.InteropContext) rx.Reactive {
	var in = func(old_state rx.Object) (func(rx.Object) rx.Object) {
		var adapter = h.Call(f, old_state)
		return func(obj rx.Object) rx.Object {
			var new_state = h.Call(adapter, obj)
			return new_state
		}
	}
	var out = func(obj rx.Object) rx.Object {
		return h.Call(g, obj)
	}
	return rx.ReactiveMorph(r, in, out)
}

func __Mutable(init rt.Value) rx.Observable {
	return rx.NewSync(func() (rx.Object, bool) {
		return rx.CreateMutable(init), true
	})
}
func __MutableRead(m rx.Mutable) rx.Observable {
	return m.Get()
}
func __MutableWrite(m rx.Mutable, v rt.Value) rx.Observable {
	return m.Set(v)
}

func __SourceFromObservable(action rx.Observable) rx.Observable {
	return action.DiscardComplete()
}
func __Connect(source rx.Observable, sink rx.Sink) rx.Observable {
	return rx.Connect(source, sink)
}

func __ComputedFromSource(following rx.Observable, head_ rt.TupleValue) rx.Observable {
	var head = rt.ListFrom(rt.SingleValueFromRecord(head_))
	return rx.Concat([] rx.Observable {
		rx.NewSyncSequence(func(next func(rx.Object))(bool,rx.Object) {
			head.ForEach(func(_ uint, item rt.Value) {
				next(item)
			})
			return true, nil
		}),
		following,
	}).DistinctUntilChanged(rt.ValueEqual)
}
func __ComputedSample(computed rx.Observable) rx.Observable {
	return computed.TakeOneAsSingleAssumeSync().Map(func(opt_ rx.Object) rx.Object {
		var opt = opt_.(rx.Optional)
		if opt.HasValue {
			return opt.Value
		} else {
			panic("something went wrong")
		}
	})
}
func __ComputedWithValueFrom(computed rx.Observable, m rx.Mutable) rx.Observable {
	return computed.ConcatMap(func(v1 rx.Object) rx.Observable {
		return m.Get().Map(func(v2 rx.Object) rx.Object {
			return rt.Tuple(v1, v2)
		})
	})
}
func __ComputedLiftAx(tuple rt.TupleValue, f rt.Value, h rt.InteropContext) rx.Observable {
	var items = make([] rx.Observable, len(tuple.Elements))
	for i, el := range tuple.Elements {
		items[i] = el.(rx.Observable)
	}
	return CombineComputedAsTupleValues(items).Map(func(values rx.Object) rx.Object {
		return h.Call(f, values)
	})
}
func __ComputedCombine(tuple rt.TupleValue) rx.Observable {
	var items = make([] rx.Observable, len(tuple.Elements))
	for i, el := range tuple.Elements {
		items[i] = el.(rx.Observable)
	}
	return CombineComputedAsTupleValues(items)
}
func __ComputedCombineList(list_ rt.Value) rx.Observable {
	var list = rt.ListFrom(list_)
	return CombineComputedAsSliceValues(list.CopyAsObservables())
}
func __ComputedMap(e rx.Observable, f rt.Value, h rt.InteropContext) rx.Observable {
	return e.Map(func(val rx.Object) rx.Object {
		return h.Call(f, val)
	}).DistinctUntilChanged(rt.ValueEqual)
}
func __ComputedSwitchMap(e rx.Observable, f rt.Value, h rt.InteropContext) rx.Observable {
	return e.SwitchMap(func(val rx.Object) rx.Observable {
		return h.Call(f, val).(rx.Observable)
	}).DistinctUntilChanged(rt.ValueEqual)
}
func __ComputedListMap(args rt.Value, ctx ([]rt.Value), h rt.InteropContext) rx.Observable {
	var cmp_ = ctx[0].(rt.FuncValue)
	var cmp = rt.Compare(func(v1 rt.Value, v2 rt.Value) ctn.Ordering {
		return rt.FromOrdering(h.Call(cmp_, rt.Tuple(v1, v2)).(rt.EnumValue))
	})
	var computed_keys_, f_ = rt.PairFromTuple2(args.(rt.TupleValue))
	var computed_keys = computed_keys_.(rx.Observable)
	var f = func(key rx.Object) rx.Observable {
		return h.Call(f_, key).(rx.Observable)
	}
	var lcp = &rx.ListCacheProvider {
		CreateMap: func() rx.Object {
			return rt.NewMap(cmp)
		},
		MapLength: func(m *rx.Object) uint {
			return (*m).(rt.Map).Size()
		},
		MapLookup: func(m *rx.Object, k rx.Object) (rx.Object, bool) {
			return (*m).(rt.Map).Lookup(k)
		},
		MapInsert: func(m *rx.Object, k rx.Object, v rx.Object) {
			*m = (*m).(rt.Map).Inserted(k, v)
		},
		MapDelete: func(m *rx.Object, k rx.Object) {
			*m = (*m).(rt.Map).Deleted(k)
		},
		MapIterate: func(m *rx.Object, f func(rx.Object, rx.Object)) {
			(*m).(rt.Map).ForEach(f)
		},
		CreateList: func(n uint) rx.Object {
			return make([]rt.Value, n)
		},
		ListFill: func(l *rx.Object, i uint, v rx.Object) {
			(*l).([]rt.Value)[i] = v
		},
		ListIterate: func(l *rx.Object, f func(uint, rx.Object)) {
			rt.ListFrom(*l).ForEach(f)
		},
	}
	return computed_keys.ListMap(lcp, f)
}
func __ComputedMemoMap(c rx.Observable, f rt.FuncValue, h rt.InteropContext) rx.Observable {
	type uninitialized struct {}
	var mapped =
		c.Map(func(val rx.Object) rx.Object {
			return h.Call(f, val)
		}).DistinctUntilChanged(rt.ValueEqual)
	return rx.NewSync(func() (rx.Object, bool) {
		return rx.CreateReactive(uninitialized {}), true
	}).SyncThen(func(obj rx.Object) rx.Observable {
		var buf = obj.(rx.Reactive)
		var in = rx.Sink(buf)
		var out = rx.DistinctWatch(buf, rt.ValueEqual).Map(func(obj rx.Object) rx.Object {
			var _, wrong = obj.(uninitialized)
			if wrong { panic("something went wrong") }
			return obj
		})
		return rx.NewYield(out).With(rx.Connect(mapped, in))
	})
}

func __Do(e rx.Observable, f rt.Value, h rt.InteropContext) rx.Observable {
	return e.SyncThen(func(val rx.Object) rx.Observable {
		return h.Call(f, val).(rx.Observable)
	})
}
func __Await(e rx.Observable, f rt.Value, h rt.InteropContext) rx.Observable {
	// TODO: no Single related logic
	// TODO: should return the last value anyway (panic if 0 value)
	return e.Then(func(val rx.Object) rx.Observable {
		return h.Call(f, val).(rx.Observable)
	})
}
func __With(main rx.Observable, side rx.Observable) rx.Observable {
	return main.With(side)
}
func __WithCanceller(f rt.Value, h rt.InteropContext) rx.Observable {
	return rx.WithCanceller(func(cancel rx.Observable) rx.Observable {
		return h.Call(f, cancel).(rx.Observable)
	})
}
func __OnCancel(o rx.Observable, disposal rx.Observable) rx.Observable {
	return o.OnCancel(disposal)
}

func __Go(f rt.Value, h rt.InteropContext) rx.Observable {
	return rx.NewGoroutineSingle(func(ctx *rx.Context) (rx.Object, bool) {
		defer recoverFromSyncCancellationPanic()
		return h.CallDetached(ctx, f.(rt.FuncValue), nil), true
	})
}
func __Async(f rt.Value, h rt.InteropContext) rx.Observable {
	return rx.NewQueuedWithContext(h.Worker2(), func(ctx *rx.Context) (rx.Object, bool) {
		defer recoverFromSyncCancellationPanic()
		return h.CallDetached(ctx, f.(rt.FuncValue), nil), true
	})
}
func __Return(v rt.Value) rx.Observable {
	return rx.NewYield(v)
}
func __Yield(av rt.Value) rx.Observable {
	var list = rt.ListFrom(av)
	return rx.NewSyncSequence(func(next func(rx.Object))(bool,rx.Object) {
		list.ForEach(func(_ uint, item rt.Value) {
			next(item)
		})
		return true, nil
	})
}
func __Const(v rt.Value) rx.Observable {
	return rx.NewPersistent(v)
}
func __Throw(err rt.Value) rx.Observable {
	return rx.Throw(err)
}

func __Random01() rx.Observable {
	return rx.NewSync(func() (rx.Object, bool) {
		return rand.Float64(), true
	})
}
var nextPUID = uint64(0)
func __PUID() rx.Observable {
	return rx.NewSync(func() (rx.Object, bool) {
		var id = nextPUID
		nextPUID += 1
		return util.GetNumberUint64(id), true
	})
}
func __UUID() rx.Observable {
	return rx.NewSync(func() (rx.Object, bool) {
		return qt.UUID(), true
	})
}
func __Wait(timeout *big.Int) rx.Observable {
	return rx.Timer(util.GetUintNumber(timeout))
}
func __Tick(interval *big.Int) rx.Observable {
	return rx.Ticker(util.GetUintNumber(interval))
}
func __Crash(v rt.Value, h rt.InteropContext) rx.Observable {
	var msg string
	switch V := v.(type) {
	case string:
		msg = V
	case error:
		msg = V.Error()
	default:
		panic("crash: unknown error value type")
	}
	const bold = "\033[1m"
	const red = "\033[31m"
	const reset = "\033[0m"
	var loc, ok = h.Location()
	return rx.NewSync(func() (rx.Object, bool) {
		if ok {
			var loc = loc.SourceLocation()
			fmt.Fprintf (
				os.Stderr, "%v*** Crash: %s at %s%v\n",
				bold+red, loc.PosDesc(), loc.FilePath(), reset,
			)
		} else {
			fmt.Fprintf (
				os.Stderr, "%v*** Crash: top-level%v\n",
				bold+red, reset,
			)
		}
		fmt.Fprintf (
			os.Stderr, "%v%s%v\n",
			bold+red, msg, reset,
		)
		os.Exit(63)
		// noinspection GoUnreachableCode
		panic("program should have crashed")
	})
}

func __Catch(e rx.Observable, f rt.Value, h rt.InteropContext) rx.Observable {
	return e.Catch(func(err rx.Object) rx.Observable {
		return h.Call(f, err).(rx.Observable)
	})
}
func __CatchNone(e rx.Observable, f rt.Value, h rt.InteropContext) rx.Observable {
	return e.CatchNone(func(err rx.Object) rx.Observable {
		return h.Call(f, err).(rx.Observable)
	}).Map(func(obj rx.Object) rx.Object {
		return Optional2Maybe(obj)
	})
}
func __CatchComplete(e rx.Observable, f rt.Value, h rt.InteropContext) rx.Observable {
	return e.CatchComplete(func(err rx.Object) rx.Observable {
		return h.Call(f, err).(rx.Observable)
	})
}
func __CatchRetry(e rx.Observable, f rt.Value, h rt.InteropContext) rx.Observable {
	return e.CatchRetry(func(err rx.Object) rx.Observable {
		return h.Call(f, err).(rx.Observable).Map(func(retry rx.Object) rx.Object {
			return rt.FromBool(retry.(rt.EnumValue))
		})
	})
}
func __CatchThrow(e rx.Observable, f rt.Value, h rt.InteropContext) rx.Observable {
	return e.CatchThrow(func(err rx.Object) rx.Object {
		return h.Call(f, err)
	})
}

func __ObservableStartWith(following rx.Observable, head_ rt.Value) rx.Observable {
	var head = rt.ListFrom(head_)
	return rx.Concat([] rx.Observable {
		rx.NewSyncSequence(func(next func(rx.Object))(bool,rx.Object) {
			head.ForEach(func(_ uint, item rt.Value) {
				next(item)
			})
			return true, nil
		}),
		following,
	})
}
func __ObservableTakeOneAsSingle(e rx.Observable) rx.Observable {
	return e.TakeOneAsSingle().Map(func(val rx.Object) rx.Object {
		var opt = val.(rx.Optional)
		if opt.HasValue {
			return rt.Some(opt.Value)
		} else {
			return rt.None()
		}
	})
}
func __ObservableWaitComplete(e rx.Observable) rx.Observable {
	return e.WaitComplete()
}
func __ObservableForever(e rx.Observable) rx.Observable {
	var repeat rx.Observable
	repeat = e.WaitComplete().Then(func(_ rx.Object) rx.Observable {
		return repeat
	})
	return repeat
}
func __ObservableMap(e rx.Observable, f rt.Value, h rt.InteropContext) rx.Observable {
	return e.Map(func(val rx.Object) rx.Object {
		return h.Call(f, val)
	})
}
func __ObservableDeflateMap(e rx.Observable, f rt.Value, h rt.InteropContext) rx.Observable {
	return e.DeflateMap(func(val rx.Object) (rx.Object, bool) {
		var maybe_mapped = h.Call(f, val).(rt.EnumValue)
		return rt.Unwrap(maybe_mapped)
	})
}
func __ObservableFilter(e rx.Observable, f rt.Value, h rt.InteropContext) rx.Observable {
	return e.Filter(func(val rx.Object) bool {
		return rt.FromBool((h.Call(f, val)).(rt.EnumValue))
	})
}
func __ObservableReduce(e rx.Observable, opts rt.TupleValue, h rt.InteropContext) rx.Observable {
	var init = opts.Elements[0]
	var f = opts.Elements[1]
	return e.Reduce(func(acc rx.Object, val rx.Object) rx.Object {
		return h.Call(f, rt.Tuple(acc, val))
	}, init)
}
func __ObservableScan(e rx.Observable, opts rt.TupleValue, h rt.InteropContext) rx.Observable {
	var init = opts.Elements[0]
	var f = opts.Elements[1]
	return e.Scan(func(acc rx.Object, val rx.Object) rx.Object {
		return h.Call(f, rt.Tuple(acc, val))
	}, init)
}
func __ObservableDebounce(e rx.Observable, d *big.Int) rx.Observable {
	return e.DebounceTime(util.GetUintNumber(d))
}
func __ObservableSwitchMap(e rx.Observable, f rt.Value, h rt.InteropContext) rx.Observable {
	return e.SwitchMap(func(val rx.Object) rx.Observable {
		return h.Call(f, val).(rx.Observable)
	})
}
func __ObservableExhaustMap(e rx.Observable, f rt.Value, h rt.InteropContext) rx.Observable {
	return e.ExhaustMap(func(val rx.Object) rx.Observable {
		return h.Call(f, val).(rx.Observable)
	})
}
func __ObservableMergeMap(e rx.Observable, f rt.Value, h rt.InteropContext) rx.Observable {
	return e.MergeMap(func(val rx.Object) rx.Observable {
		return h.Call(f, val).(rx.Observable)
	})
}
func __ObservableConcatMap(e rx.Observable, f rt.Value, h rt.InteropContext) rx.Observable {
	return e.ConcatMap(func(val rx.Object) rx.Observable {
		return h.Call(f, val).(rx.Observable)
	})
}
func __ObservableMixMap(e rx.Observable, n *big.Int, f rt.Value, h rt.InteropContext) rx.Observable {
	return e.MixMap(func(val rx.Object) rx.Observable {
		return h.Call(f, val).(rx.Observable)
	}, util.GetUintNumber(n))
}
func __ObservableMerge(av rt.Value) rx.Observable {
	var list = rt.ListFrom(av)
	return rx.Merge(list.CopyAsObservables())
}
func __ObservableConcat(av rt.Value) rx.Observable {
	var list = rt.ListFrom(av)
	return rx.Concat(list.CopyAsObservables())
}
func __ObservableDistinctUntilChanged(a rx.Observable, ctx ([] rt.Value), h rt.InteropContext) rx.Observable {
	return a.DistinctUntilChanged(func(obj1 rx.Object, obj2 rx.Object) bool {
		var pair = rt.Tuple(obj1, obj2)
		return rt.FromBool(h.Call(ctx[0], pair).(rt.EnumValue))
	})
}
func __ObservableWithLatestFrom(a rx.Observable, values rx.Observable) rx.Observable {
	return a.WithLatestFrom(values).Map(func(p rx.Object) rx.Object {
		var pair = p.(rx.Pair)
		return rt.Tuple(pair.First, Optional2Maybe(pair.Second))
	})
}
func __ObservableCombineLatest(tuple rt.TupleValue) rx.Observable {
	var items = make([] rx.Observable, len(tuple.Elements))
	for i, el := range tuple.Elements {
		items[i] = el.(rx.Observable)
	}
	return rx.CombineLatestWaitReady(items).Map(func(values_ rx.Object) rx.Object {
		var values = values_.([]rt.Value)
		return rt.TupleOf(values)
	})
}
func __ObservableCombineLatestList(list_ rt.Value) rx.Observable {
	var list = rt.ListFrom(list_)
	return rx.CombineLatestWaitReady(list.CopyAsObservables())
}


